package com.berje.vkalarm.vksdk;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.zip.GZIPInputStream;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.berje.vkalarm.vksdk.model.Audio;
import com.berje.vkalarm.vksdk.model.Group;
import com.berje.vkalarm.vksdk.model.GroupAudioFeed;

import android.util.Log;

public class Api {

	static final String TAG = "Kate.Api";

	public static final String BASE_URL = "https://api.vk.com/method/";

	public Api(String access_token, String api_id) {
		this.access_token = access_token;
		this.api_id = api_id;
	}

	String access_token;
	String api_id;

	// TODO: it's not faster, even slower on slow devices. Maybe we should add
	// an option to disable it. It's only good for paid internet connection.
	static boolean enable_compression = true;

	/*** utils methods ***/
	private void checkError(JSONObject root, String url) throws JSONException, KException {
		if (!root.isNull("error")) {
			JSONObject error = root.getJSONObject("error");
			int code = error.getInt("error_code");
			String message = error.getString("error_msg");
			KException e = new KException(code, message, url);
			if (code == 14) {
				e.captcha_img = error.optString("captcha_img");
				e.captcha_sid = error.optString("captcha_sid");
			}
			throw e;
		}
	}

	private JSONObject sendRequest(Params params) throws IOException, MalformedURLException, JSONException, KException {
		return sendRequest(params, false);
	}

	private final static int MAX_TRIES = 3;

	private JSONObject sendRequest(Params params, boolean is_post) throws IOException, MalformedURLException, JSONException, KException {
		String url = getSignedUrl(params, is_post);
		String body = "";
		if (is_post)
			body = params.getParamsString(is_post);
		Log.i(TAG, "url=" + url);
		if (body.length() != 0)
			Log.i(TAG, "body=" + body);
		String response = "";
		for (int i = 1; i <= MAX_TRIES; ++i) {
			try {
				if (i != 1)
					Log.i(TAG, "try " + i);
				response = sendRequestInternal(url, body, is_post);
				break;
			} catch (javax.net.ssl.SSLException ex) {
				processNetworkException(i, ex);
			} catch (java.net.SocketException ex) {
				processNetworkException(i, ex);
			}
		}
		Log.i(TAG, "response=" + response);
		JSONObject root = new JSONObject(response);
		checkError(root, url);
		return root;
	}

	private void processNetworkException(int i, IOException ex) throws IOException {
		ex.printStackTrace();
		if (i == MAX_TRIES)
			throw ex;
	}

	private String sendRequestInternal(String url, String body, boolean is_post) throws IOException, MalformedURLException,
			WrongResponseCodeException {
		HttpURLConnection connection = null;
		try {
			connection = (HttpURLConnection) new URL(url).openConnection();
			connection.setConnectTimeout(30000);
			connection.setReadTimeout(30000);
			connection.setUseCaches(false);
			connection.setDoOutput(is_post);
			connection.setDoInput(true);
			connection.setRequestMethod(is_post ? "POST" : "GET");
			if (enable_compression)
				connection.setRequestProperty("Accept-Encoding", "gzip");
			if (is_post)
				connection.getOutputStream().write(body.getBytes("UTF-8"));
			int code = connection.getResponseCode();
			Log.i(TAG, "code=" + code);
			// It may happen due to keep-alive problem
			// http://stackoverflow.com/questions/1440957/httpurlconnection-getresponsecode-returns-1-on-second-invocation
			if (code == -1)
				throw new WrongResponseCodeException("Network error");
			// может стоит проверить на код 200
			// on error can also read error stream from connection.
			InputStream is = new BufferedInputStream(connection.getInputStream(), 8192);
			String enc = connection.getHeaderField("Content-Encoding");
			if (enc != null && enc.equalsIgnoreCase("gzip"))
				is = new GZIPInputStream(is);
			String response = Utils.convertStreamToString(is);
			return response;
		} finally {
			if (connection != null)
				connection.disconnect();
		}
	}

	private long getDefaultStartTime() {
		long now = System.currentTimeMillis() / 1000L;// unixtime
		return now - 31 * 24 * 60 * 60;// month ago
	}

	private String getSignedUrl(Params params, boolean is_post) {
		String args = "";
		if (!is_post)
			args = params.getParamsString(is_post);

		// add access_token
		if (args.length() != 0)
			args += "&";
		args += "access_token=" + access_token;

		return BASE_URL + params.getMethod_name() + "?" + args;
	}

	public static String unescape(String text) {
		if (text == null)
			return null;
		return text.replace("&amp;", "&").replace("&quot;", "\"").replace("<br>", "\n").replace("&gt;", ">").replace("&lt;", "<")
				.replace("&#39;", "'").replace("<br/>", "\n").replace("&ndash;", "-").replace("&#33;", "!").trim();
		// возможно тут могут быть любые коды после &#, например были: 092 -
		// backslash \
	}

	public static String unescapeWithSmiles(String text) {
		return unescape(text)
		// May be useful to someone
		// .replace("\uD83D\uDE0A", ":-)")
		// .replace("\uD83D\uDE03", ":D")
		// .replace("\uD83D\uDE09", ";-)")
		// .replace("\uD83D\uDE06", "xD")
		// .replace("\uD83D\uDE1C", ";P")
		// .replace("\uD83D\uDE0B", ":p")
		// .replace("\uD83D\uDE0D", "8)")
		// .replace("\uD83D\uDE0E", "B)")
		//
		// .replace("\ud83d\ude12", ":(") //F0 9F 98 92
		// .replace("\ud83d\ude0f", ":]") //F0 9F 98 8F
		// .replace("\ud83d\ude14", "3(") //F0 9F 98 94
		// .replace("\ud83d\ude22", ":'(") //F0 9F 98 A2
		// .replace("\ud83d\ude2d", ":_(") //F0 9F 98 AD
		// .replace("\ud83d\ude29", ":((") //F0 9F 98 A9
		// .replace("\ud83d\ude28", ":o") //F0 9F 98 A8
		// .replace("\ud83d\ude10", ":|") //F0 9F 98 90
		//
		// .replace("\ud83d\ude0c", "3)") //F0 9F 98 8C
		// .replace("\ud83d\ude20", ">(") //F0 9F 98 A0
		// .replace("\ud83d\ude21", ">((") //F0 9F 98 A1
		// .replace("\ud83d\ude07", "O:)") //F0 9F 98 87
		// .replace("\ud83d\ude30", ";o") //F0 9F 98 B0
		// .replace("\ud83d\ude32", "8o") //F0 9F 98 B2
		// .replace("\ud83d\ude33", "8|") //F0 9F 98 B3
		// .replace("\ud83d\ude37", ":X") //F0 9F 98 B7
		//
		// .replace("\ud83d\ude1a", ":*") //F0 9F 98 9A
		// .replace("\ud83d\ude08", "}:)") //F0 9F 98 88
		// .replace("\u2764", "<3") //E2 9D A4
		// .replace("\ud83d\udc4d", ":like:") //F0 9F 91 8D
		// .replace("\ud83d\udc4e", ":dislike:") //F0 9F 91 8E
		// .replace("\u261d", ":up:") //E2 98 9D
		// .replace("\u270c", ":v:") //E2 9C 8C
		// .replace("\ud83d\udc4c", ":ok:") //F0 9F 91 8C
		;
	}

	// http://vk.com/developers.php?oid=-1&p=groups.getById
	public ArrayList<Group> getGroups(Collection<Long> uids, String domain, String fields) throws MalformedURLException, IOException,
			JSONException, KException {
		if (uids == null && domain == null)
			return null;
		if (uids.size() == 0 && domain == null)
			return null;
		Params params = new Params("groups.getById");
		String str_uids;
		if (uids != null && uids.size() > 0)
			str_uids = arrayToString(uids);
		else
			str_uids = domain;
		params.put("gids", str_uids);
		params.put("fields", fields); // Possible values:
										// place,wiki_page,city,country,description,start_date,finish_date,site
		JSONObject root = sendRequest(params);
		JSONArray array = root.optJSONArray("response");
		return Group.parseGroups(array);
	}

	<T> String arrayToString(Collection<T> items) {
		if (items == null)
			return null;
		String str_cids = "";
		for (Object item : items) {
			if (str_cids.length() != 0)
				str_cids += ',';
			str_cids += item;
		}
		return str_cids;
	}

	// http://vkontakte.ru/developers.php?o=-1&p=newsfeed.get
	// always returns about 33-35 items
	public GroupAudioFeed getGroupMusicNews(String gid, long start_time, long count, long end_time, String captcha_key, String captcha_sid)
			throws MalformedURLException, IOException, JSONException, KException {

		Params params = new Params("newsfeed.get");
		params.put("source_ids", gid);
		params.put("filters", "post");
		params.put("start_time", (start_time == -1) ? getDefaultStartTime() : start_time);
		if (end_time != -1)
			params.put("end_time", end_time);
		if (count != 0)
			params.put("count", count);

		addCaptchaParams(captcha_key, captcha_sid, params);

		JSONObject root = sendRequest(params);

		return GroupAudioFeed.parse(root, false);
	}

	// http://vk.com/developers.php?oid=-1&p=audio.getById
	public List<Audio> getAudioById(String audios) throws MalformedURLException, IOException, JSONException, KException {
		Log.d(TAG, "API -- getAudioById: " + audios);

		Params params = new Params("audio.getById");
		params.put("audios", audios);
		JSONObject root = sendRequest(params);
		JSONArray array = root.optJSONArray("response");
		return parseAudioList(array, 0);
	}

	private ArrayList<Audio> parseAudioList(JSONArray array, int type_array) // type_array
																				// must
																				// be
																				// 0
																				// or
																				// 1
			throws JSONException {
		ArrayList<Audio> audios = new ArrayList<Audio>();
		if (array != null) {
			for (int i = type_array; i < array.length(); ++i) { // get(0) is
																// integer, it
																// is audio
																// count
				JSONObject o = (JSONObject) array.get(i);
				audios.add(Audio.parse(o));
			}
		}
		return audios;
	}

	private void addCaptchaParams(String captcha_key, String captcha_sid, Params params) {
		params.put("captcha_sid", captcha_sid);
		params.put("captcha_key", captcha_key);
	}

	// http://vkontakte.ru/developers.php?oid=-1&p=groups.get
	public ArrayList<Group> getUserGroups(Long user_id) throws MalformedURLException, IOException, JSONException, KException {
		Params params = new Params("groups.get");
		params.put("extended", "1");
		params.put("uid", user_id);
		JSONObject root = sendRequest(params);
		ArrayList<Group> groups = new ArrayList<Group>();
		JSONArray array = root.optJSONArray("response");
		// if there are no groups "response" will not be array
		if (array == null)
			return groups;
		groups = Group.parseGroups(array);
		return groups;
	}

	public List<Audio> getAudiosWithUrl(List<Audio> audios) throws MalformedURLException, IOException, JSONException, KException {
		List<Audio> ret = null;
		StringBuilder sb = new StringBuilder();

		boolean was = false;
		for (Audio a : audios) {
			if (was)
				sb.append(",");
			sb.append(a.getOwner_id());
			sb.append("_");
			sb.append(a.getAid());
			was = true;
		}
		ret = getAudioById(sb.toString());
		Log.d(TAG, "audios: " + ret);
		return ret;
	}

	public List<Audio> getFilledAudios(List<Audio> audios) throws MalformedURLException, IOException, JSONException, KException {
		StringBuilder sb = new StringBuilder();
		boolean was = false;
		for (Audio a : audios) {
			if (was)
				sb.append(",");
			sb.append(a.getId());
			was = true;
		}

		Log.d(TAG, "complex audio ids: [" + sb.toString() + "]");
		return getAudioById(sb.toString());
	}

}